#include "timer_peripheral.h"
#include "NuMicro.h"

static const uint32_t scMaxCmpValue = 0x00FFFFF;

void TimerInitByFre(TIMER_AVAILABLE_LIST tim, uint16_t fre)
{
	SYS_UnlockReg();
	
	if (tim == TIMER_PERIPHERAL_0) {
		CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_PCLK0, 20);
		CLK_EnableModuleClock(TMR0_MODULE);
		SystemCoreClockUpdate();
		
		TIMER_Open(TIMER0, TIMER_PERIODIC_MODE, fre);
	}
	else if (tim == TIMER_PERIPHERAL_1) {
		CLK_SetModuleClock(TMR1_MODULE, CLK_CLKSEL1_TMR1SEL_PCLK0, 20);
		CLK_EnableModuleClock(TMR1_MODULE);
		SystemCoreClockUpdate();
		
		TIMER_Open(TIMER1, TIMER_PERIODIC_MODE, fre);
	}
	else if (tim == TIMER_PERIPHERAL_2) {
		CLK_SetModuleClock(TMR2_MODULE, CLK_CLKSEL1_TMR2SEL_PCLK1, 20);
		CLK_EnableModuleClock(TMR2_MODULE);
		SystemCoreClockUpdate();
		
		TIMER_Open(TIMER2, TIMER_PERIODIC_MODE, fre);
	}
	
	SYS_LockReg();
}

void TimerInitByPeriod(TIMER_AVAILABLE_LIST tim, uint16_t period)
{
	uint32_t inputClk;
    uint32_t prescaleVal = 0x00FF;
    uint32_t cmpValue = 0;
	
	SYS_UnlockReg();
	
	if (tim == TIMER_PERIPHERAL_0) {
		CLK_SetModuleClock(TMR0_MODULE, CLK_CLKSEL1_TMR0SEL_PCLK0, 20);
		CLK_EnableModuleClock(TMR0_MODULE);
		SystemCoreClockUpdate();
		inputClk = TIMER_GetModuleClock(TIMER0);
	}
	else if (tim == TIMER_PERIPHERAL_1) {
		CLK_SetModuleClock(TMR1_MODULE, CLK_CLKSEL1_TMR1SEL_PCLK0, 20);
		CLK_EnableModuleClock(TMR1_MODULE);
		SystemCoreClockUpdate();
		inputClk = TIMER_GetModuleClock(TIMER1);
	}
	else if (tim == TIMER_PERIPHERAL_2) {
		CLK_SetModuleClock(TMR2_MODULE, CLK_CLKSEL1_TMR2SEL_PCLK1, 20);
		CLK_EnableModuleClock(TMR2_MODULE);
		SystemCoreClockUpdate();
		inputClk = TIMER_GetModuleClock(TIMER2);
	}
	
	cmpValue = inputClk / 1000 / (prescaleVal + 1) * period / 1000;
	while (cmpValue > scMaxCmpValue) {
		prescaleVal--;
		cmpValue = inputClk / 1000 / (prescaleVal + 1) * period / 1000;
	}
	if (cmpValue < 2) {
		cmpValue = 2;
	}
	
	if (tim == TIMER_PERIPHERAL_0) {
		TIMER0->CTL = TIMER_PERIODIC_MODE | prescaleVal;
		TIMER0->CMP = cmpValue;
	}
	else if (tim == TIMER_PERIPHERAL_1) {
		TIMER1->CTL = TIMER_PERIODIC_MODE | prescaleVal;
		TIMER1->CMP = cmpValue;
	}
	else if (tim == TIMER_PERIPHERAL_2) {
		TIMER2->CTL = TIMER_PERIODIC_MODE | prescaleVal;
		TIMER2->CMP = cmpValue;
	}
	
	SYS_LockReg();
}

void TimerInterruptEnable(TIMER_AVAILABLE_LIST tim, uint32_t priority)
{
	uint32_t prioritygroup = 0x00U;
	
	if (tim == TIMER_PERIPHERAL_0) {
		if (NVIC_GetActive(TMR0_IRQn) == 0) {
			NVIC_EnableIRQ(TMR0_IRQn);
			prioritygroup = NVIC_GetPriorityGrouping();
			NVIC_SetPriority(TMR0_IRQn, NVIC_EncodePriority(prioritygroup, priority, 0));
			TIMER_EnableInt(TIMER0);
		}
		else {
			NVIC_SetPriority(TMR0_IRQn, priority);
		}
	}
	else if (tim == TIMER_PERIPHERAL_1) {
		if (NVIC_GetActive(TMR1_IRQn) == 0) {
			NVIC_EnableIRQ(TMR1_IRQn);
			prioritygroup = NVIC_GetPriorityGrouping();
			NVIC_SetPriority(TMR1_IRQn, NVIC_EncodePriority(prioritygroup, priority, 0));
			TIMER_EnableInt(TIMER1);
		}
		else {
			NVIC_SetPriority(TMR1_IRQn, priority);
		}
	}
	else if (tim == TIMER_PERIPHERAL_2) {
		if (NVIC_GetActive(TMR2_IRQn) == 0) {
			NVIC_EnableIRQ(TMR2_IRQn);
			prioritygroup = NVIC_GetPriorityGrouping();
			NVIC_SetPriority(TMR2_IRQn, NVIC_EncodePriority(prioritygroup, priority, 0));
			TIMER_EnableInt(TIMER2);
		}
		else {
			NVIC_SetPriority(TMR2_IRQn, priority);
		}
	}
}

void TimerInterruptDisable(TIMER_AVAILABLE_LIST tim)
{
	if (tim == TIMER_PERIPHERAL_0) {
		NVIC_DisableIRQ(TMR0_IRQn);
		TIMER_DisableInt(TIMER0);
	}
	else if (tim == TIMER_PERIPHERAL_1) {
		NVIC_DisableIRQ(TMR1_IRQn);
		TIMER_DisableInt(TIMER1);
	}
	else if (tim == TIMER_PERIPHERAL_2) {
		NVIC_DisableIRQ(TMR2_IRQn);
		TIMER_DisableInt(TIMER2);
	}
}

void TimerClose(TIMER_AVAILABLE_LIST tim)
{
	switch (tim) {
		case TIMER_PERIPHERAL_0:
			TIMER_Close(TIMER0);
			break;
		case TIMER_PERIPHERAL_1:
			TIMER_Close(TIMER1);
			break;
		case TIMER_PERIPHERAL_2:
			TIMER_Close(TIMER2);
			break;
		default:
			break;
	}
}

void TimerStart(TIMER_AVAILABLE_LIST tim)
{
	if (tim == TIMER_PERIPHERAL_0) {
		TIMER_Start(TIMER0);
	}
	else if (tim == TIMER_PERIPHERAL_1) {
		TIMER_Start(TIMER1);
	}
	else if (tim == TIMER_PERIPHERAL_2) {
		TIMER_Start(TIMER2);
	}
}

void TimerStop(TIMER_AVAILABLE_LIST tim)
{
	if (tim == TIMER_PERIPHERAL_0) {
		TIMER_Stop(TIMER0);
	}
	else if (tim == TIMER_PERIPHERAL_1) {
		TIMER_Stop(TIMER1);
	}
	else if (tim == TIMER_PERIPHERAL_2) {
		TIMER_Stop(TIMER2);
	}
}

#if TIMER_TIMESTAMP_EN
void TimerTimestampInit(void)
{
	uint32_t clk, psc;
	
	SYS_UnlockReg();
	
	CLK_SetModuleClock(TMR2_MODULE, CLK_CLKSEL1_TMR2SEL_PCLK1, 20);
	CLK_EnableModuleClock(TMR2_MODULE);
	SystemCoreClockUpdate();
	
	clk = TIMER_GetModuleClock(TIMER2);
	clk = clk / 1000000;
	TIMER2->CTL = TIMER_PERIODIC_MODE;
	if (clk > 128) {
		psc = clk / 2;
	}
	else {
		psc = clk - 1;
	}
	TIMER2->CTL |= psc;
	TIMER2->CMP = 0xFFFFFF;
	TIMER2->CNT = 0;
	
	SYS_LockReg();
}

void TimerTimestampRun(void)
{
	TIMER2->CNT = 0;
	TIMER2->CTL |= TIMER_CTL_CNTEN_Msk;
}

void TimerTimestampStop(void)
{
	TIMER2->CTL &= ~TIMER_CTL_CNTEN_Msk;
}

uint32_t TimerTimestampGetCNT(void)
{
	return TIMER2->CNT;
}
#endif
